﻿using ApprovalFlow.Interface;
using ApprovalFlow.Models;
using ApprovalFlow.Primitives;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace ApprovalFlow.Services
{
    public class WorkflowExecutor : IWorkflowExecutor
    {

        private readonly IServiceProvider _serviceProvider;
        private readonly IWorkflowRegistry _workflowRegistry;
        private readonly IWorkflowRepository _workflowRepository;
        private readonly Dictionary<string, WorkflowStep> _stepBodyCollection;
        private readonly IExecutionResultProcessor _executionResultProcessor;

        public WorkflowExecutor(
            IServiceProvider serviceProvider, IWorkflowRegistry workflowRegistry, IWorkflowRepository workflowRepository,
            IExecutionResultProcessor executionResultProcessor)
        {
            _stepBodyCollection = new Dictionary<string, WorkflowStep>
            {
                ["startEvent"] = new WorkflowStep<Start>(),
                ["approval"] = new WorkflowStep<Approval>(),
                ["cc"] = new WorkflowStep<CC>(),
                ["connector"] = new WorkflowStep<Connector>(),
                ["transaction"] = new WorkflowStep<Transaction>(),
                ["waitFor"] = new WorkflowStep<WaitFor>(),
            };
            _serviceProvider = serviceProvider;
            _workflowRegistry = workflowRegistry;
            _workflowRepository = workflowRepository;
            _executionResultProcessor = executionResultProcessor;
        }

        public async Task Execute(WorkflowInstance workflow, CancellationToken cancellationToken = default)
        {
            var exePointers = workflow.ExecutionPointers.Where(x => x.Active).ToArray();
            var def = this._workflowRegistry.GetDefinition(workflow.ProcessCode, workflow.Version);

            foreach (var pointer in exePointers)
            {
                WorkflowStep step = this._stepBodyCollection[pointer.Processor];
                if (!InitializeStep(workflow, step, pointer))
                    continue;
                var through = await ExecuteStep(workflow, step, pointer, def);
                if (!through)
                    return;
            }
            if (workflow.ExecutionPointers.Exists(x => x.Active))
                await Execute(workflow, cancellationToken);
        }


        private bool InitializeStep(WorkflowInstance workflow, WorkflowStep step, ExecutionPointer pointer)
        {
            switch (step.InitForExecution(workflow, pointer))
            {
                case ExecutionPipelineDirective.Defer:
                    return false;
                case ExecutionPipelineDirective.EndWorkflow:
                    workflow.Status = WorkflowStatus.Completed;
                    workflow.CompleteTime = DateTime.Now;
                    return false;
            }


            if (pointer.Status != PointerStatus.Running)
                pointer.Status = PointerStatus.Running;
            if (!pointer.StartTime.HasValue)
                pointer.StartTime = DateTime.Now;

            return true;
        }

        /// <summary>
        /// 执行步骤
        /// </summary>
        private async Task<bool> ExecuteStep(WorkflowInstance workflow, WorkflowStep step, ExecutionPointer pointer, WorkflowDefinition def)
        {
            IStepExecutionContext context = new StepExecutionContext
            {
                Workflow = workflow,
                Step = step,
                PersistenceData = pointer.PersistenceData,
                ExecutionPointer = pointer,
                //Item = pointer.ContextItem,
                //CancellationToken = cancellationToken
            };

            IStepBody stepBody = step.ConstructBody(this._serviceProvider);
            switch (step.BeforeExecute(context, pointer, stepBody))
            {
                case ExecutionPipelineDirective.Defer:
                    return false;
                case ExecutionPipelineDirective.EndWorkflow:
                    return false;
                case ExecutionPipelineDirective.Next:
                    break;
            }

            //if (stepBody is ITaskStep)
            //{
            //    EventSubscription subscription = new()
            //    {
            //        Id = Guid.NewGuid().ToString(),
            //        InstanceId = workflow.InstanceId,
            //        EventKey = pointer.Id,
            //        EventName = pointer.StepName,
            //        ExecutionPointerId = pointer.Id,
            //        StepId = pointer.StepID,
            //    };
            //    await _subscriptionRepository.CreateEventSubscription(subscription);
            //}
            var result = await stepBody.RunAsync(context);

            // 如果返回了事件的值，则需要添加事件
            await _executionResultProcessor.ProcessExecutionResult(workflow, def, pointer, step, result);
            // 执行步骤后事件。
            step.AfterExecute(context, result, pointer);
            return result.Proceed;
        }
    }
}
